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