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