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