switch to gnsrecord reclaim records for OIDC clients
[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 (0 != strcmp (rd[0].data,
899                      handle->oidc->redirect_uri))
900       continue;
901     tmp = GNUNET_strdup (rd[0].data);
902     pos = strrchr (tmp,
903                    (unsigned char) '.');
904     *pos = '\0';
905     handle->redirect_prefix = GNUNET_strdup (tmp);
906     tmp_key_str = pos + 1;
907     pos = strchr (tmp_key_str,
908                   (unsigned char) '/');
909     *pos = '\0';
910     handle->redirect_suffix = GNUNET_strdup (pos + 1);
911
912     GNUNET_STRINGS_string_to_data (tmp_key_str,
913                                    strlen (tmp_key_str),
914                                    &redirect_zone,
915                                    sizeof (redirect_zone));
916
917     GNUNET_NAMESTORE_zone_to_name (handle->namestore_handle,
918                                    &handle->priv_key,
919                                    &redirect_zone,
920                                    &get_client_name_error,
921                                    handle,
922                                    &get_client_name_result,
923                                    handle);
924     GNUNET_free (tmp);
925     return;
926   }
927   handle->emsg = GNUNET_strdup("server_error");
928   handle->edesc = GNUNET_strdup("Server cannot generate ticket, redirect uri not found.");
929   GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
930 }
931
932 /**
933  * Issues ticket and redirects to relying party with the authorization code as
934  * parameter. Otherwise redirects with error
935  */
936 static void
937 oidc_ticket_issue_cb (void* cls,
938                       const struct GNUNET_RECLAIM_Ticket *ticket)
939 {
940   struct RequestHandle *handle = cls;
941
942   handle->idp_op = NULL;
943   handle->ticket = *ticket;
944   if (NULL == ticket)
945   {
946     handle->emsg = GNUNET_strdup("server_error");
947     handle->edesc = GNUNET_strdup("Server cannot generate ticket.");
948     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
949     return;
950   }
951   handle->gns_op = GNUNET_GNS_lookup (handle->gns_handle,
952                                       "+",
953                                       &handle->oidc->client_pkey,
954                                       GNUNET_GNSRECORD_TYPE_RECLAIM_OIDC_REDIRECT,
955                                       GNUNET_GNS_LO_DEFAULT,
956                                       &lookup_redirect_uri_result,
957                                       handle);
958
959 }
960
961 static void
962 oidc_collect_finished_cb (void *cls)
963 {
964   struct RequestHandle *handle = cls;
965   handle->attr_it = NULL;
966   handle->ticket_it = NULL;
967   if (NULL == handle->attr_list->list_head)
968   {
969     handle->emsg = GNUNET_strdup("invalid_scope");
970     handle->edesc = GNUNET_strdup("The requested scope is not available.");
971     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
972     return;
973   }
974   handle->idp_op = GNUNET_RECLAIM_ticket_issue (handle->idp,
975                                                 &handle->priv_key,
976                                                 &handle->oidc->client_pkey,
977                                                 handle->attr_list,
978                                                 &oidc_ticket_issue_cb,
979                                                 handle);
980 }
981
982
983 /**
984  * Collects all attributes for an ego if in scope parameter
985  */
986 static void
987 oidc_attr_collect (void *cls,
988                    const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
989                    const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
990 {
991   struct RequestHandle *handle = cls;
992   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry *le;
993   char* scope_variables;
994   char* scope_variable;
995   char delimiter[]=" ";
996
997   if ( (NULL == attr->name) || (NULL == attr->data) )
998   {
999     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1000     return;
1001   }
1002
1003   scope_variables = GNUNET_strdup(handle->oidc->scope);
1004   scope_variable = strtok (scope_variables, delimiter);
1005   while (NULL != scope_variable)
1006   {
1007     if ( 0 == strcmp (attr->name, scope_variable) )
1008     {
1009       break;
1010     }
1011     scope_variable = strtok (NULL, delimiter);
1012   }
1013   if ( NULL == scope_variable )
1014   {
1015     GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1016     GNUNET_free(scope_variables);
1017     return;
1018   }
1019   GNUNET_free(scope_variables);
1020
1021   le = GNUNET_new(struct GNUNET_RECLAIM_ATTRIBUTE_ClaimListEntry);
1022   le->claim = GNUNET_RECLAIM_ATTRIBUTE_claim_new (attr->name, attr->type,
1023                                                   attr->data, attr->data_size);
1024   GNUNET_CONTAINER_DLL_insert(handle->attr_list->list_head,
1025                               handle->attr_list->list_tail, le);
1026   GNUNET_RECLAIM_get_attributes_next (handle->attr_it);
1027 }
1028
1029
1030 /**
1031  * Checks time and cookie and redirects accordingly
1032  */
1033 static void
1034 login_check (void *cls)
1035 {
1036   struct RequestHandle *handle = cls;
1037   struct GNUNET_TIME_Absolute current_time, *relog_time;
1038   struct GNUNET_CRYPTO_EcdsaPublicKey pubkey, ego_pkey;
1039   struct GNUNET_HashCode cache_key;
1040   char *identity_cookie;
1041
1042   GNUNET_asprintf (&identity_cookie, "Identity=%s", handle->oidc->login_identity);
1043   GNUNET_CRYPTO_hash (identity_cookie, strlen (identity_cookie), &cache_key);
1044   GNUNET_free(identity_cookie);
1045   //No login time for identity -> redirect to login
1046   if ( GNUNET_YES
1047        == GNUNET_CONTAINER_multihashmap_contains (OIDC_identity_login_time,
1048                                                   &cache_key) )
1049   {
1050     relog_time = GNUNET_CONTAINER_multihashmap_get (OIDC_identity_login_time,
1051                                                     &cache_key);
1052     current_time = GNUNET_TIME_absolute_get ();
1053     // 30 min after old login -> redirect to login
1054     if ( current_time.abs_value_us <= relog_time->abs_value_us )
1055     {
1056       if ( GNUNET_OK
1057            != GNUNET_CRYPTO_ecdsa_public_key_from_string (
1058                                                           handle->oidc->login_identity,
1059                                                           strlen (handle->oidc->login_identity), &pubkey) )
1060       {
1061         handle->emsg = GNUNET_strdup("invalid_cookie");
1062         handle->edesc = GNUNET_strdup(
1063                                       "The cookie of a login identity is not valid");
1064         GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1065         return;
1066       }
1067       // iterate over egos and compare their public key
1068       for (handle->ego_entry = handle->ego_head;
1069            NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1070       {
1071         GNUNET_IDENTITY_ego_get_public_key (handle->ego_entry->ego, &ego_pkey);
1072         if ( 0
1073              == memcmp (&ego_pkey, &pubkey,
1074                         sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1075         {
1076           handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (
1077                                                                    handle->ego_entry->ego);
1078           handle->resp_object = GNUNET_JSONAPI_document_new ();
1079           handle->idp = GNUNET_RECLAIM_connect (cfg);
1080           handle->attr_list = GNUNET_new(
1081                                          struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1082           handle->attr_it = GNUNET_RECLAIM_get_attributes_start (
1083                                                                  handle->idp, &handle->priv_key, &oidc_iteration_error, handle,
1084                                                                  &oidc_attr_collect, handle, &oidc_collect_finished_cb, handle);
1085           return;
1086         }
1087       }
1088       //handle->emsg = GNUNET_strdup("invalid_cookie");
1089       //handle->edesc = GNUNET_strdup(
1090       //                              "The cookie of the login identity is not valid");
1091       //GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1092       GNUNET_SCHEDULER_add_now (&login_redirection,handle);
1093       return;
1094     }
1095   }
1096 }
1097
1098 /**
1099  * Iteration over all results finished, build final
1100  * response.
1101  *
1102  * @param cls the `struct RequestHandle`
1103  */
1104 static void
1105 build_authz_response (void *cls)
1106 {
1107   struct RequestHandle *handle = cls;
1108   struct GNUNET_HashCode cache_key;
1109
1110   char *expected_scope;
1111   char delimiter[]=" ";
1112   int number_of_ignored_parameter, iterator;
1113
1114
1115   // REQUIRED value: redirect_uri
1116   GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1117                       &cache_key);
1118   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1119                                                            &cache_key))
1120   {
1121     handle->emsg=GNUNET_strdup("invalid_request");
1122     handle->edesc=GNUNET_strdup("missing parameter redirect_uri");
1123     GNUNET_SCHEDULER_add_now (&do_error, handle);
1124     return;
1125   }
1126   handle->oidc->redirect_uri = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1127                                                                                 &cache_key));
1128
1129   // REQUIRED value: response_type
1130   GNUNET_CRYPTO_hash (OIDC_RESPONSE_TYPE_KEY, strlen (OIDC_RESPONSE_TYPE_KEY),
1131                       &cache_key);
1132   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1133                                                            &cache_key))
1134   {
1135     handle->emsg=GNUNET_strdup("invalid_request");
1136     handle->edesc=GNUNET_strdup("missing parameter response_type");
1137     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1138     return;
1139   }
1140   handle->oidc->response_type = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1141                                                                   &cache_key);
1142   handle->oidc->response_type = GNUNET_strdup (handle->oidc->response_type);
1143
1144   // REQUIRED value: scope
1145   GNUNET_CRYPTO_hash (OIDC_SCOPE_KEY, strlen (OIDC_SCOPE_KEY), &cache_key);
1146   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1147                                                            &cache_key))
1148   {
1149     handle->emsg=GNUNET_strdup("invalid_request");
1150     handle->edesc=GNUNET_strdup("missing parameter scope");
1151     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1152     return;
1153   }
1154   handle->oidc->scope = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1155                                                           &cache_key);
1156   handle->oidc->scope = GNUNET_strdup(handle->oidc->scope);
1157
1158   //OPTIONAL value: nonce
1159   GNUNET_CRYPTO_hash (OIDC_NONCE_KEY, strlen (OIDC_NONCE_KEY), &cache_key);
1160   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1161                                                             &cache_key))
1162   {
1163     handle->oidc->nonce = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1164                                                             &cache_key);
1165     handle->oidc->nonce = GNUNET_strdup (handle->oidc->nonce);
1166   }
1167
1168   //TODO check other values if needed
1169   number_of_ignored_parameter = sizeof(OIDC_ignored_parameter_array) / sizeof(char *);
1170   for( iterator = 0; iterator < number_of_ignored_parameter; iterator++ )
1171   {
1172     GNUNET_CRYPTO_hash (OIDC_ignored_parameter_array[iterator],
1173                         strlen(OIDC_ignored_parameter_array[iterator]),
1174                         &cache_key);
1175     if(GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains(handle->rest_handle->url_param_map,
1176                                                             &cache_key))
1177     {
1178       handle->emsg=GNUNET_strdup("access_denied");
1179       GNUNET_asprintf (&handle->edesc, "Server will not handle parameter: %s",
1180                        OIDC_ignored_parameter_array[iterator]);
1181       GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1182       return;
1183     }
1184   }
1185
1186   // Checks if response_type is 'code'
1187   if( 0 != strcmp( handle->oidc->response_type, OIDC_EXPECTED_AUTHORIZATION_RESPONSE_TYPE ) )
1188   {
1189     handle->emsg=GNUNET_strdup("unsupported_response_type");
1190     handle->edesc=GNUNET_strdup("The authorization server does not support "
1191                                 "obtaining this authorization code.");
1192     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1193     return;
1194   }
1195
1196   // Checks if scope contains 'openid'
1197   expected_scope = GNUNET_strdup(handle->oidc->scope);
1198   char* test;
1199   test = strtok (expected_scope, delimiter);
1200   while (NULL != test)
1201   {
1202     if ( 0 == strcmp (OIDC_EXPECTED_AUTHORIZATION_SCOPE, expected_scope) )
1203     {
1204       break;
1205     }
1206     test = strtok (NULL, delimiter);
1207   }
1208   if (NULL == test)
1209   {
1210     handle->emsg = GNUNET_strdup("invalid_scope");
1211     handle->edesc=GNUNET_strdup("The requested scope is invalid, unknown, or "
1212                                 "malformed.");
1213     GNUNET_SCHEDULER_add_now (&do_redirect_error, handle);
1214     GNUNET_free(expected_scope);
1215     return;
1216   }
1217
1218   GNUNET_free(expected_scope);
1219
1220   if( NULL != handle->oidc->login_identity )
1221   {
1222     GNUNET_SCHEDULER_add_now(&login_check,handle);
1223     return;
1224   }
1225
1226   GNUNET_SCHEDULER_add_now(&login_redirection,handle);
1227 }
1228
1229 /**
1230  * Responds to authorization GET and url-encoded POST request
1231  *
1232  * @param con_handle the connection handle
1233  * @param url the url
1234  * @param cls the RequestHandle
1235  */
1236 static void
1237 authorize_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1238                     const char* url,
1239                     void *cls)
1240 {
1241   struct RequestHandle *handle = cls;
1242   struct GNUNET_HashCode cache_key;
1243   struct EgoEntry *tmp_ego;
1244   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv_key;
1245   struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1246
1247   cookie_identity_interpretation(handle);
1248
1249   //RECOMMENDED value: state - REQUIRED for answers
1250   GNUNET_CRYPTO_hash (OIDC_STATE_KEY, strlen (OIDC_STATE_KEY), &cache_key);
1251   if (GNUNET_YES == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1252                                                             &cache_key))
1253   {
1254     handle->oidc->state = GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1255                                                             &cache_key);
1256     handle->oidc->state = GNUNET_strdup (handle->oidc->state);
1257   }
1258
1259   // REQUIRED value: client_id
1260   GNUNET_CRYPTO_hash (OIDC_CLIENT_ID_KEY, strlen (OIDC_CLIENT_ID_KEY),
1261                       &cache_key);
1262   if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1263                                                            &cache_key))
1264   {
1265     handle->emsg=GNUNET_strdup("invalid_request");
1266     handle->edesc=GNUNET_strdup("missing parameter client_id");
1267     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1268     GNUNET_SCHEDULER_add_now (&do_error, handle);
1269     return;
1270   }
1271   handle->oidc->client_id = GNUNET_strdup (GNUNET_CONTAINER_multihashmap_get(handle->rest_handle->url_param_map,
1272                                                                              &cache_key));
1273
1274   if ( GNUNET_OK
1275        != GNUNET_CRYPTO_ecdsa_public_key_from_string (handle->oidc->client_id,
1276                                                       strlen (handle->oidc->client_id),
1277                                                       &handle->oidc->client_pkey) )
1278   {
1279     handle->emsg = GNUNET_strdup("unauthorized_client");
1280     handle->edesc = GNUNET_strdup("The client is not authorized to request an "
1281                                   "authorization code using this method.");
1282     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1283     GNUNET_SCHEDULER_add_now (&do_error, handle);
1284     return;
1285   }
1286
1287
1288   if ( NULL == handle->ego_head )
1289   {
1290     handle->emsg = GNUNET_strdup("server_error");
1291     handle->edesc = GNUNET_strdup ("Egos are missing");
1292     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1293     GNUNET_SCHEDULER_add_now (&do_error, handle);
1294     return;
1295   }
1296
1297   handle->ego_entry = handle->ego_head;
1298   handle->priv_key = *GNUNET_IDENTITY_ego_get_private_key (handle->ego_head->ego);
1299   //If we know this identity, translated the corresponding TLD
1300   //TODO: We might want to have a reverse lookup functionality for TLDs?
1301   for (tmp_ego = handle->ego_head; NULL != tmp_ego; tmp_ego = tmp_ego->next)
1302   {
1303     priv_key = GNUNET_IDENTITY_ego_get_private_key (tmp_ego->ego);
1304     GNUNET_CRYPTO_ecdsa_key_get_public (priv_key,
1305                                         &pkey);
1306     if ( 0 == memcmp (&pkey, &handle->oidc->client_pkey,
1307                       sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)) )
1308     {
1309       handle->tld = GNUNET_strdup (tmp_ego->identifier);
1310       handle->ego_entry = handle->ego_tail;
1311     }
1312   } 
1313   GNUNET_SCHEDULER_add_now (&build_authz_response, handle);
1314 }
1315
1316 /**
1317  * Combines an identity with a login time and responds OK to login request
1318  *
1319  * @param con_handle the connection handle
1320  * @param url the url
1321  * @param cls the RequestHandle
1322  */
1323 static void
1324 login_cont (struct GNUNET_REST_RequestHandle *con_handle,
1325             const char* url,
1326             void *cls)
1327 {
1328   struct MHD_Response *resp = GNUNET_REST_create_response ("");
1329   struct RequestHandle *handle = cls;
1330   struct GNUNET_HashCode cache_key;
1331   struct GNUNET_TIME_Absolute *current_time;
1332   struct GNUNET_TIME_Absolute *last_time;
1333   char* cookie;
1334   json_t *root;
1335   json_error_t error;
1336   json_t *identity;
1337   char term_data[handle->rest_handle->data_size+1];
1338   term_data[handle->rest_handle->data_size] = '\0';
1339   GNUNET_memcpy (term_data, handle->rest_handle->data, handle->rest_handle->data_size);
1340   root = json_loads (term_data, JSON_DECODE_ANY, &error);
1341   identity = json_object_get (root, "identity");
1342   if ( json_is_string(identity) )
1343   {
1344     GNUNET_asprintf (&cookie, "Identity=%s", json_string_value (identity));
1345     MHD_add_response_header (resp, "Set-Cookie", cookie);
1346     MHD_add_response_header (resp, "Access-Control-Allow-Methods", "POST");
1347     GNUNET_CRYPTO_hash (cookie, strlen (cookie), &cache_key);
1348
1349     current_time = GNUNET_new(struct GNUNET_TIME_Absolute);
1350     *current_time = GNUNET_TIME_relative_to_absolute (
1351                                                       GNUNET_TIME_relative_multiply (GNUNET_TIME_relative_get_second_ (),
1352                                                                                      5));
1353     last_time = GNUNET_CONTAINER_multihashmap_get(OIDC_identity_login_time, &cache_key);
1354     if (NULL != last_time)
1355     {
1356       GNUNET_free(last_time);
1357     }
1358     GNUNET_CONTAINER_multihashmap_put (
1359                                        OIDC_identity_login_time, &cache_key, current_time,
1360                                        GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1361
1362     handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1363     GNUNET_free(cookie);
1364   }
1365   else
1366   {
1367     handle->proc (handle->proc_cls, resp, MHD_HTTP_BAD_REQUEST);
1368   }
1369   json_decref (root);
1370   GNUNET_SCHEDULER_add_now (&cleanup_handle_delayed, handle);
1371   return;
1372 }
1373
1374 static int 
1375 check_authorization (struct RequestHandle *handle,
1376                      struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1377 {
1378   struct GNUNET_HashCode cache_key;
1379   char *authorization;
1380   char *credentials;
1381   char *basic_authorization;
1382   char *client_id;
1383   char *pass;
1384   char *expected_pass;
1385   int client_exists = GNUNET_NO;
1386
1387   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1388                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1389                       &cache_key);
1390   if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->header_param_map,
1391                                                             &cache_key) )
1392   {
1393     handle->emsg=GNUNET_strdup("invalid_client");
1394     handle->edesc=GNUNET_strdup("missing authorization");
1395     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1396     return GNUNET_SYSERR;
1397   }
1398   authorization = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->header_param_map,
1399                                                      &cache_key);
1400
1401   //split header in "Basic" and [content]
1402   credentials = strtok (authorization, " ");
1403   if (0 != strcmp ("Basic", credentials))
1404   {
1405     handle->emsg=GNUNET_strdup("invalid_client");
1406     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1407     return GNUNET_SYSERR;
1408   }
1409   credentials = strtok(NULL, " ");
1410   if (NULL == credentials)
1411   {
1412     handle->emsg=GNUNET_strdup("invalid_client");
1413     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1414     return GNUNET_SYSERR;
1415   }
1416   GNUNET_STRINGS_base64_decode (credentials,
1417                                 strlen (credentials),
1418                                 (void**)&basic_authorization);
1419
1420   if ( NULL == basic_authorization )
1421   {
1422     handle->emsg=GNUNET_strdup("invalid_client");
1423     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1424     return GNUNET_SYSERR;
1425   }
1426   client_id = strtok (basic_authorization, ":");
1427   if ( NULL == client_id )
1428   {
1429     GNUNET_free_non_null(basic_authorization);
1430     handle->emsg=GNUNET_strdup("invalid_client");
1431     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1432     return GNUNET_SYSERR;
1433   }
1434   pass = strtok (NULL, ":");
1435   if (NULL == pass)
1436   {
1437     GNUNET_free_non_null(basic_authorization);
1438     handle->emsg=GNUNET_strdup("invalid_client");
1439     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1440     return GNUNET_SYSERR;
1441   }
1442
1443   //check client password
1444   if ( GNUNET_OK
1445        == GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1446                                                  "psw", &expected_pass) )
1447   {
1448     if (0 != strcmp (expected_pass, pass))
1449     {
1450       GNUNET_free_non_null(basic_authorization);
1451       GNUNET_free(expected_pass);
1452       handle->emsg=GNUNET_strdup("invalid_client");
1453       handle->response_code = MHD_HTTP_UNAUTHORIZED;
1454       return GNUNET_SYSERR;
1455     }
1456     GNUNET_free(expected_pass);
1457   }
1458   else
1459   {
1460     GNUNET_free_non_null(basic_authorization);
1461     handle->emsg = GNUNET_strdup("server_error");
1462     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1463     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1464     return GNUNET_SYSERR;
1465   }
1466
1467   //check client_id
1468   for (handle->ego_entry = handle->ego_head; NULL != handle->ego_entry->next; )
1469   {
1470     if ( 0 == strcmp(handle->ego_entry->keystring, client_id))
1471     {
1472       client_exists = GNUNET_YES;
1473       break;
1474     }
1475     handle->ego_entry = handle->ego_entry->next;
1476   }
1477   if (GNUNET_NO == client_exists)
1478   {
1479     GNUNET_free_non_null(basic_authorization);
1480     handle->emsg=GNUNET_strdup("invalid_client");
1481     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1482     return GNUNET_SYSERR;
1483   }
1484   GNUNET_STRINGS_string_to_data (client_id,
1485                                  strlen(client_id),
1486                                  cid,
1487                                  sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1488
1489   GNUNET_free (basic_authorization);
1490   return GNUNET_OK;
1491 }
1492
1493 static int
1494 ego_exists (struct RequestHandle *handle,
1495             struct GNUNET_CRYPTO_EcdsaPublicKey *test_key)
1496 {
1497   struct EgoEntry *ego_entry;
1498   struct GNUNET_CRYPTO_EcdsaPublicKey pub_key;
1499
1500   for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next)
1501   {
1502     GNUNET_IDENTITY_ego_get_public_key (ego_entry->ego, &pub_key);
1503     if (0 == memcmp (&pub_key,
1504                      test_key,
1505                      sizeof(struct GNUNET_CRYPTO_EcdsaPublicKey)))
1506     {
1507       break;
1508     }
1509   }
1510   if (NULL == ego_entry)
1511     return GNUNET_NO;
1512   return GNUNET_YES;
1513 }
1514
1515 static void
1516 store_ticket_reference (const struct RequestHandle *handle,
1517                         const char* access_token,
1518                         const struct GNUNET_RECLAIM_Ticket *ticket,
1519                         const struct GNUNET_CRYPTO_EcdsaPublicKey *cid)
1520 {
1521   struct GNUNET_HashCode cache_key;
1522   char *id_ticket_combination;
1523   char *ticket_string;
1524   char *client_id;
1525
1526   GNUNET_CRYPTO_hash(access_token, strlen(access_token), &cache_key);
1527   client_id = GNUNET_STRINGS_data_to_string_alloc (cid,
1528                                                    sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey));
1529   ticket_string = GNUNET_STRINGS_data_to_string_alloc (ticket,
1530                                                        sizeof (struct GNUNET_RECLAIM_Ticket));
1531   GNUNET_asprintf(&id_ticket_combination,
1532                   "%s;%s",
1533                   client_id,
1534                   ticket_string);
1535   GNUNET_CONTAINER_multihashmap_put(OIDC_interpret_access_token,
1536                                     &cache_key,
1537                                     id_ticket_combination,
1538                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_REPLACE);
1539
1540   GNUNET_free (client_id);
1541   GNUNET_free (ticket_string);
1542 }
1543
1544 /**
1545  * Responds to token url-encoded POST request
1546  *
1547  * @param con_handle the connection handle
1548  * @param url the url
1549  * @param cls the RequestHandle
1550  */
1551 static void
1552 token_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1553                 const char* url,
1554                 void *cls)
1555 {
1556   struct RequestHandle *handle = cls;
1557   struct GNUNET_TIME_Relative expiration_time;
1558   struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList *cl; 
1559   struct GNUNET_RECLAIM_Ticket *ticket;
1560   struct GNUNET_CRYPTO_EcdsaPublicKey cid;
1561   struct GNUNET_HashCode cache_key;
1562   struct MHD_Response *resp;
1563   char *grant_type;
1564   char *code;
1565   char *json_response;
1566   char *id_token;
1567   char *access_token;
1568   char *jwt_secret;
1569   char *nonce;
1570   int i = 1;
1571
1572   /*
1573    * Check Authorization
1574    */
1575   if (GNUNET_SYSERR == check_authorization (handle,
1576                                             &cid))
1577   {
1578     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1579                 "OIDC authorization for token endpoint failed\n");
1580     GNUNET_SCHEDULER_add_now (&do_error, handle);
1581     return;
1582   }
1583
1584   /*
1585    * Check parameter
1586    */
1587
1588   //TODO Do not allow multiple equal parameter names
1589   //REQUIRED grant_type
1590   GNUNET_CRYPTO_hash (OIDC_GRANT_TYPE_KEY, strlen (OIDC_GRANT_TYPE_KEY), &cache_key);
1591   if (GNUNET_NO ==
1592       GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1593                                               &cache_key))
1594   {
1595     handle->emsg = GNUNET_strdup("invalid_request");
1596     handle->edesc = GNUNET_strdup("missing parameter grant_type");
1597     handle->response_code = MHD_HTTP_BAD_REQUEST;
1598     GNUNET_SCHEDULER_add_now (&do_error, handle);
1599     return;
1600   }
1601   grant_type = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1602                                                   &cache_key);
1603
1604   //REQUIRED code
1605   GNUNET_CRYPTO_hash (OIDC_CODE_KEY, strlen (OIDC_CODE_KEY), &cache_key);
1606   if (GNUNET_NO ==
1607       GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1608                                               &cache_key))
1609   {
1610     handle->emsg = GNUNET_strdup("invalid_request");
1611     handle->edesc = GNUNET_strdup("missing parameter code");
1612     handle->response_code = MHD_HTTP_BAD_REQUEST;
1613     GNUNET_SCHEDULER_add_now (&do_error, handle);
1614     return;
1615   }
1616   code = GNUNET_CONTAINER_multihashmap_get (handle->rest_handle->url_param_map,
1617                                             &cache_key);
1618
1619   //REQUIRED redirect_uri
1620   GNUNET_CRYPTO_hash (OIDC_REDIRECT_URI_KEY, strlen (OIDC_REDIRECT_URI_KEY),
1621                       &cache_key);
1622   if (GNUNET_NO ==
1623       GNUNET_CONTAINER_multihashmap_contains (handle->rest_handle->url_param_map,
1624                                               &cache_key) )
1625   {
1626     handle->emsg = GNUNET_strdup("invalid_request");
1627     handle->edesc = GNUNET_strdup("missing parameter redirect_uri");
1628     handle->response_code = MHD_HTTP_BAD_REQUEST;
1629     GNUNET_SCHEDULER_add_now (&do_error, handle);
1630     return;
1631   }
1632
1633   //Check parameter grant_type == "authorization_code"
1634   if (0 != strcmp(OIDC_GRANT_TYPE_VALUE, grant_type))
1635   {
1636     handle->emsg=GNUNET_strdup("unsupported_grant_type");
1637     handle->response_code = MHD_HTTP_BAD_REQUEST;
1638     GNUNET_SCHEDULER_add_now (&do_error, handle);
1639     return;
1640   }
1641   GNUNET_CRYPTO_hash (code, strlen (code), &cache_key);
1642   if (GNUNET_SYSERR ==
1643       GNUNET_CONTAINER_multihashmap_put (OIDC_ticket_once,
1644                                          &cache_key,
1645                                          &i,
1646                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY) )
1647   {
1648     handle->emsg = GNUNET_strdup("invalid_request");
1649     handle->edesc = GNUNET_strdup("Cannot use the same code more than once");
1650     handle->response_code = MHD_HTTP_BAD_REQUEST;
1651     GNUNET_SCHEDULER_add_now (&do_error, handle);
1652     return;
1653   }
1654
1655   //decode code
1656   if(GNUNET_OK != OIDC_parse_authz_code (&cid,
1657                                          code,
1658                                          &ticket,
1659                                          &nonce))
1660   {
1661     handle->emsg = GNUNET_strdup("invalid_request");
1662     handle->edesc = GNUNET_strdup("invalid code");
1663     handle->response_code = MHD_HTTP_BAD_REQUEST;
1664     GNUNET_SCHEDULER_add_now (&do_error, handle);
1665     return;
1666   }
1667
1668   //create jwt
1669   if (GNUNET_OK !=
1670       GNUNET_CONFIGURATION_get_value_time(cfg,
1671                                           "reclaim-rest-plugin",
1672                                           "expiration_time",
1673                                           &expiration_time))
1674   {
1675     handle->emsg = GNUNET_strdup("server_error");
1676     handle->edesc = GNUNET_strdup ("gnunet configuration failed");
1677     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1678     GNUNET_SCHEDULER_add_now (&do_error, handle);
1679     GNUNET_free(ticket);
1680     return;
1681   }
1682
1683
1684   //TODO OPTIONAL acr,amr,azp
1685   if (GNUNET_NO == ego_exists (handle,
1686                                &ticket->audience))
1687   {
1688     handle->emsg = GNUNET_strdup("invalid_request");
1689     handle->edesc = GNUNET_strdup("invalid code...");
1690     handle->response_code = MHD_HTTP_BAD_REQUEST;
1691     GNUNET_SCHEDULER_add_now (&do_error, handle);
1692     GNUNET_free(ticket);
1693   }
1694   if ( GNUNET_OK
1695        != GNUNET_CONFIGURATION_get_value_string (cfg, "reclaim-rest-plugin",
1696                                                  "jwt_secret", &jwt_secret) )
1697   {
1698     handle->emsg = GNUNET_strdup("invalid_request");
1699     handle->edesc = GNUNET_strdup("No signing secret configured!");
1700     handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
1701     GNUNET_SCHEDULER_add_now (&do_error, handle);
1702     GNUNET_free(ticket);
1703     return;
1704   }
1705   //TODO We should collect the attributes here. cl always empty
1706   cl = GNUNET_new (struct GNUNET_RECLAIM_ATTRIBUTE_ClaimList);
1707   id_token = OIDC_id_token_new (&ticket->audience,
1708                                 &ticket->identity,
1709                                 cl,
1710                                 &expiration_time,
1711                                 (NULL != nonce) ? nonce : NULL,
1712                                 jwt_secret);
1713   access_token = OIDC_access_token_new (); 
1714   OIDC_build_token_response (access_token,
1715                              id_token,
1716                              &expiration_time,
1717                              &json_response);
1718
1719   store_ticket_reference (handle,
1720                           access_token,
1721                           ticket,
1722                           &cid);
1723   resp = GNUNET_REST_create_response (json_response);
1724   MHD_add_response_header (resp, "Cache-Control", "no-store");
1725   MHD_add_response_header (resp, "Pragma", "no-cache");
1726   MHD_add_response_header (resp, "Content-Type", "application/json");
1727   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
1728   GNUNET_RECLAIM_ATTRIBUTE_list_destroy(cl);
1729   GNUNET_free(access_token);
1730   GNUNET_free(json_response);
1731   GNUNET_free(ticket);
1732   GNUNET_free(id_token);
1733   GNUNET_SCHEDULER_add_now(&cleanup_handle_delayed, handle);
1734 }
1735
1736 /**
1737  * Collects claims and stores them in handle
1738  */
1739 static void
1740 consume_ticket (void *cls,
1741                 const struct GNUNET_CRYPTO_EcdsaPublicKey *identity,
1742                 const struct GNUNET_RECLAIM_ATTRIBUTE_Claim *attr)
1743 {
1744   struct RequestHandle *handle = cls;
1745   char *tmp_value;
1746   json_t *value;
1747
1748   if (NULL == identity)
1749   {
1750     GNUNET_SCHEDULER_add_now (&return_userinfo_response, handle);
1751     return;
1752   }
1753
1754   tmp_value = GNUNET_RECLAIM_ATTRIBUTE_value_to_string (attr->type,
1755                                                         attr->data,
1756                                                         attr->data_size);
1757
1758   value = json_string (tmp_value);
1759
1760
1761   json_object_set_new (handle->oidc->response,
1762                        attr->name,
1763                        value);
1764   GNUNET_free (tmp_value);
1765 }
1766
1767 /**
1768  * Responds to userinfo GET and url-encoded POST request
1769  *
1770  * @param con_handle the connection handle
1771  * @param url the url
1772  * @param cls the RequestHandle
1773  */
1774 static void
1775 userinfo_endpoint (struct GNUNET_REST_RequestHandle *con_handle,
1776                    const char* url, void *cls)
1777 {
1778   //TODO expiration time
1779   struct RequestHandle *handle = cls;
1780   char delimiter[] = " ";
1781   char delimiter_db[] = ";";
1782   struct GNUNET_HashCode cache_key;
1783   char *authorization, *authorization_type, *authorization_access_token;
1784   char *client_ticket, *client, *ticket_str;
1785   struct GNUNET_RECLAIM_Ticket *ticket;
1786
1787   GNUNET_CRYPTO_hash (OIDC_AUTHORIZATION_HEADER_KEY,
1788                       strlen (OIDC_AUTHORIZATION_HEADER_KEY),
1789                       &cache_key);
1790   if ( GNUNET_NO
1791        == GNUNET_CONTAINER_multihashmap_contains (
1792                                                   handle->rest_handle->header_param_map, &cache_key) )
1793   {
1794     handle->emsg = GNUNET_strdup("invalid_token");
1795     handle->edesc = GNUNET_strdup("No Access Token");
1796     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1797     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1798     return;
1799   }
1800   authorization = GNUNET_CONTAINER_multihashmap_get (
1801                                                      handle->rest_handle->header_param_map, &cache_key);
1802
1803   //split header in "Bearer" and access_token
1804   authorization = GNUNET_strdup(authorization);
1805   authorization_type = strtok (authorization, delimiter);
1806   if ( 0 != strcmp ("Bearer", authorization_type) )
1807   {
1808     handle->emsg = GNUNET_strdup("invalid_token");
1809     handle->edesc = GNUNET_strdup("No Access Token");
1810     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1811     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1812     GNUNET_free(authorization);
1813     return;
1814   }
1815   authorization_access_token = strtok (NULL, delimiter);
1816   if ( NULL == authorization_access_token )
1817   {
1818     handle->emsg = GNUNET_strdup("invalid_token");
1819     handle->edesc = GNUNET_strdup("No Access Token");
1820     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1821     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1822     GNUNET_free(authorization);
1823     return;
1824   }
1825
1826   GNUNET_CRYPTO_hash (authorization_access_token,
1827                       strlen (authorization_access_token),
1828                       &cache_key);
1829   if ( GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains (OIDC_interpret_access_token,
1830                                                             &cache_key) )
1831   {
1832     handle->emsg = GNUNET_strdup("invalid_token");
1833     handle->edesc = GNUNET_strdup("The Access Token expired");
1834     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1835     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1836     GNUNET_free(authorization);
1837     return;
1838   }
1839
1840   client_ticket = GNUNET_CONTAINER_multihashmap_get(OIDC_interpret_access_token,
1841                                                     &cache_key);
1842   client_ticket = GNUNET_strdup(client_ticket);
1843   client = strtok(client_ticket,delimiter_db);
1844   if (NULL == client)
1845   {
1846     handle->emsg = GNUNET_strdup("invalid_token");
1847     handle->edesc = GNUNET_strdup("The Access Token expired");
1848     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1849     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1850     GNUNET_free(authorization);
1851     GNUNET_free(client_ticket);
1852     return;
1853   }
1854   handle->ego_entry = handle->ego_head;
1855   for(; NULL != handle->ego_entry; handle->ego_entry = handle->ego_entry->next)
1856   {
1857     if (0 == strcmp(handle->ego_entry->keystring,client))
1858     {
1859       break;
1860     }
1861   }
1862   if (NULL == handle->ego_entry)
1863   {
1864     handle->emsg = GNUNET_strdup("invalid_token");
1865     handle->edesc = GNUNET_strdup("The Access Token expired");
1866     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1867     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1868     GNUNET_free(authorization);
1869     GNUNET_free(client_ticket);
1870     return;
1871   }
1872   ticket_str = strtok(NULL, delimiter_db);
1873   if (NULL == ticket_str)
1874   {
1875     handle->emsg = GNUNET_strdup("invalid_token");
1876     handle->edesc = GNUNET_strdup("The Access Token expired");
1877     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1878     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1879     GNUNET_free(authorization);
1880     GNUNET_free(client_ticket);
1881     return;
1882   }
1883   ticket = GNUNET_new(struct GNUNET_RECLAIM_Ticket);
1884   if ( GNUNET_OK
1885        != GNUNET_STRINGS_string_to_data (ticket_str,
1886                                          strlen (ticket_str),
1887                                          ticket,
1888                                          sizeof(struct GNUNET_RECLAIM_Ticket)))
1889   {
1890     handle->emsg = GNUNET_strdup("invalid_token");
1891     handle->edesc = GNUNET_strdup("The Access Token expired");
1892     handle->response_code = MHD_HTTP_UNAUTHORIZED;
1893     GNUNET_SCHEDULER_add_now (&do_userinfo_error, handle);
1894     GNUNET_free(ticket);
1895     GNUNET_free(authorization);
1896     GNUNET_free(client_ticket);
1897     return;
1898   }
1899
1900   handle->idp = GNUNET_RECLAIM_connect (cfg);
1901   handle->oidc->response = json_object();
1902   json_object_set_new( handle->oidc->response, "sub", json_string( handle->ego_entry->keystring));
1903   handle->idp_op = GNUNET_RECLAIM_ticket_consume (
1904                                                   handle->idp,
1905                                                   GNUNET_IDENTITY_ego_get_private_key (handle->ego_entry->ego),
1906                                                   ticket,
1907                                                   consume_ticket,
1908                                                   handle);
1909   GNUNET_free(ticket);
1910   GNUNET_free(authorization);
1911   GNUNET_free(client_ticket);
1912
1913 }
1914
1915
1916 /**
1917  * Handle rest request
1918  *
1919  * @param handle the request handle
1920  */
1921 static void
1922 init_cont (struct RequestHandle *handle)
1923 {
1924   struct GNUNET_REST_RequestHandlerError err;
1925   static const struct GNUNET_REST_RequestHandler handlers[] = {
1926     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint},
1927     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_AUTHORIZE, &authorize_endpoint}, //url-encoded
1928     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_LOGIN, &login_cont},
1929     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_TOKEN, &token_endpoint },
1930     {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1931     {MHD_HTTP_METHOD_POST, GNUNET_REST_API_NS_USERINFO, &userinfo_endpoint },
1932     {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_OIDC,
1933       &options_cont},
1934     GNUNET_REST_HANDLER_END
1935   };
1936
1937   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
1938                                                handlers,
1939                                                &err,
1940                                                handle))
1941   {
1942     handle->response_code = err.error_code;
1943     GNUNET_SCHEDULER_add_now (&do_error, handle);
1944   }
1945 }
1946
1947 /**
1948  * If listing is enabled, prints information about the egos.
1949  *
1950  * This function is initially called for all egos and then again
1951  * whenever a ego's identifier changes or if it is deleted.  At the
1952  * end of the initial pass over all egos, the function is once called
1953  * with 'NULL' for 'ego'. That does NOT mean that the callback won't
1954  * be invoked in the future or that there was an error.
1955  *
1956  * When used with 'GNUNET_IDENTITY_create' or 'GNUNET_IDENTITY_get',
1957  * this function is only called ONCE, and 'NULL' being passed in
1958  * 'ego' does indicate an error (i.e. name is taken or no default
1959  * value is known).  If 'ego' is non-NULL and if '*ctx'
1960  * is set in those callbacks, the value WILL be passed to a subsequent
1961  * call to the identity callback of 'GNUNET_IDENTITY_connect' (if
1962  * that one was not NULL).
1963  *
1964  * When an identity is renamed, this function is called with the
1965  * (known) ego but the NEW identifier.
1966  *
1967  * When an identity is deleted, this function is called with the
1968  * (known) ego and "NULL" for the 'identifier'.  In this case,
1969  * the 'ego' is henceforth invalid (and the 'ctx' should also be
1970  * cleaned up).
1971  *
1972  * @param cls closure
1973  * @param ego ego handle
1974  * @param ctx context for application to store data for this ego
1975  *                 (during the lifetime of this process, initially NULL)
1976  * @param identifier identifier assigned by the user for this ego,
1977  *                   NULL if the user just deleted the ego and it
1978  *                   must thus no longer be used
1979  */
1980 static void
1981 list_ego (void *cls,
1982           struct GNUNET_IDENTITY_Ego *ego,
1983           void **ctx,
1984           const char *identifier)
1985 {
1986   struct RequestHandle *handle = cls;
1987   struct EgoEntry *ego_entry;
1988   struct GNUNET_CRYPTO_EcdsaPublicKey pk;
1989
1990   if ((NULL == ego) && (ID_REST_STATE_INIT == handle->state))
1991   {
1992     handle->state = ID_REST_STATE_POST_INIT;
1993     init_cont (handle);
1994     return;
1995   }
1996   if (ID_REST_STATE_INIT == handle->state) {
1997     ego_entry = GNUNET_new (struct EgoEntry);
1998     GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
1999     ego_entry->keystring =
2000       GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2001     ego_entry->ego = ego;
2002     ego_entry->identifier = GNUNET_strdup (identifier);
2003     GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
2004     return;
2005   }
2006   /* Ego renamed or added */
2007   if (identifier != NULL) {
2008     for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2009       if (ego_entry->ego == ego) {
2010         /* Rename */
2011         GNUNET_free (ego_entry->identifier);
2012         ego_entry->identifier = GNUNET_strdup (identifier);
2013         break;
2014       }
2015     }
2016     if (NULL == ego_entry) {
2017       /* Add */
2018       ego_entry = GNUNET_new (struct EgoEntry);
2019       GNUNET_IDENTITY_ego_get_public_key (ego, &pk);
2020       ego_entry->keystring =
2021         GNUNET_CRYPTO_ecdsa_public_key_to_string (&pk);
2022       ego_entry->ego = ego;
2023       ego_entry->identifier = GNUNET_strdup (identifier);
2024       GNUNET_CONTAINER_DLL_insert_tail(handle->ego_head,handle->ego_tail, ego_entry);
2025     }
2026   } else {
2027     /* Delete */
2028     for (ego_entry = handle->ego_head; NULL != ego_entry; ego_entry = ego_entry->next) {
2029       if (ego_entry->ego == ego)
2030         break;
2031     }
2032     if (NULL != ego_entry)
2033       GNUNET_CONTAINER_DLL_remove(handle->ego_head,handle->ego_tail, ego_entry);
2034   }
2035
2036 }
2037
2038 static void
2039 rest_identity_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
2040                               GNUNET_REST_ResultProcessor proc,
2041                               void *proc_cls)
2042 {
2043   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
2044   handle->oidc = GNUNET_new (struct OIDC_Variables);
2045   if ( NULL == OIDC_identity_login_time )
2046     OIDC_identity_login_time = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2047   if ( NULL == OIDC_identity_grants )
2048     OIDC_identity_grants = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2049   if ( NULL == OIDC_ticket_once )
2050     OIDC_ticket_once = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2051   if ( NULL == OIDC_interpret_access_token )
2052     OIDC_interpret_access_token = GNUNET_CONTAINER_multihashmap_create (10, GNUNET_NO);
2053   handle->response_code = 0;
2054   handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
2055   handle->proc_cls = proc_cls;
2056   handle->proc = proc;
2057   handle->state = ID_REST_STATE_INIT;
2058   handle->rest_handle = rest_handle;
2059
2060   handle->url = GNUNET_strdup (rest_handle->url);
2061   if (handle->url[strlen (handle->url)-1] == '/')
2062     handle->url[strlen (handle->url)-1] = '\0';
2063   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2064               "Connecting...\n");
2065   handle->identity_handle = GNUNET_IDENTITY_connect (cfg,
2066                                                      &list_ego,
2067                                                      handle);
2068   handle->gns_handle = GNUNET_GNS_connect (cfg);
2069   handle->namestore_handle = GNUNET_NAMESTORE_connect (cfg);
2070   handle->timeout_task =
2071     GNUNET_SCHEDULER_add_delayed (handle->timeout,
2072                                   &do_timeout,
2073                                   handle);
2074   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2075               "Connected\n");
2076 }
2077
2078 /**
2079  * Entry point for the plugin.
2080  *
2081  * @param cls Config info
2082  * @return NULL on error, otherwise the plugin context
2083  */
2084 void *
2085 libgnunet_plugin_rest_openid_connect_init (void *cls)
2086 {
2087   static struct Plugin plugin;
2088   struct GNUNET_REST_Plugin *api;
2089
2090   cfg = cls;
2091   if (NULL != plugin.cfg)
2092     return NULL;                /* can only initialize once! */
2093   memset (&plugin, 0, sizeof (struct Plugin));
2094   plugin.cfg = cfg;
2095   api = GNUNET_new (struct GNUNET_REST_Plugin);
2096   api->cls = &plugin;
2097   api->name = GNUNET_REST_API_NS_OIDC;
2098   api->process_request = &rest_identity_process_request;
2099   GNUNET_asprintf (&allow_methods,
2100                    "%s, %s, %s, %s, %s",
2101                    MHD_HTTP_METHOD_GET,
2102                    MHD_HTTP_METHOD_POST,
2103                    MHD_HTTP_METHOD_PUT,
2104                    MHD_HTTP_METHOD_DELETE,
2105                    MHD_HTTP_METHOD_OPTIONS);
2106
2107   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2108               _("Identity Provider REST API initialized\n"));
2109   return api;
2110 }
2111
2112
2113 /**
2114  * Exit point from the plugin.
2115  *
2116  * @param cls the plugin context (as returned by "init")
2117  * @return always NULL
2118  */
2119 void *
2120 libgnunet_plugin_rest_openid_connect_done (void *cls)
2121 {
2122   struct GNUNET_REST_Plugin *api = cls;
2123   struct Plugin *plugin = api->cls;
2124   plugin->cfg = NULL;
2125
2126   struct GNUNET_CONTAINER_MultiHashMapIterator *hashmap_it;
2127   void *value = NULL;
2128   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (
2129                                                               OIDC_identity_login_time);
2130   while (GNUNET_YES ==
2131          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2132   {
2133     if (NULL != value)
2134       GNUNET_free(value);
2135   }
2136   GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_login_time);
2137   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_identity_grants);
2138   while (GNUNET_YES ==
2139          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2140   {
2141     if (NULL != value)
2142       GNUNET_free(value);
2143   }
2144   GNUNET_CONTAINER_multihashmap_destroy(OIDC_identity_grants);
2145   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_ticket_once);
2146   while (GNUNET_YES ==
2147          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2148   {
2149     if (NULL != value)
2150       GNUNET_free(value);
2151   }
2152   GNUNET_CONTAINER_multihashmap_destroy(OIDC_ticket_once);
2153   hashmap_it = GNUNET_CONTAINER_multihashmap_iterator_create (OIDC_interpret_access_token);
2154   while (GNUNET_YES ==
2155          GNUNET_CONTAINER_multihashmap_iterator_next (hashmap_it, NULL, value))
2156   {
2157     if (NULL != value)
2158       GNUNET_free(value);
2159   }
2160   GNUNET_CONTAINER_multihashmap_destroy(OIDC_interpret_access_token);
2161   GNUNET_CONTAINER_multihashmap_iterator_destroy(hashmap_it);
2162   GNUNET_free_non_null (allow_methods);
2163   GNUNET_free (api);
2164   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2165               "Identity Provider REST plugin is finished\n");
2166   return NULL;
2167 }
2168
2169 /* end of plugin_rest_identity_provider.c */