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