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